// @HEADER
// ***********************************************************************
//
//             SiYuan: A numerical PDE solver
//                 Copyright (2022) YUAN Xi
//
// ***********************************************************************
// @HEADER

#ifndef EVALUATOR_MATERIAL_IMPL_HPP
#define EVALUATOR_MATERIAL_IMPL_HPP

#include "Phalanx_DataLayout_MDALayout.hpp"

namespace SiYuan {

//**********************************************************************
template<typename EvalT, typename Traits>
VariableEvaluator<EvalT, Traits>::
VariableEvaluator(std::shared_ptr<XYZLib::variable<double>>& pval, std::string& varname, int& wksize, int& np, int& nitem):
  _value( pval ), _fieldValue( varname, 
	    Teuchos::rcp(new PHX::MDALayout<panzer::Cell,panzer::IP>(wksize,np)) )
{
  this->addEvaluatedField(_fieldValue);
  _isConstant = _value->isConstant();
  _size = _value->size();
}

//**********************************************************************
template<typename EvalT, typename Traits>
void
VariableEvaluator<EvalT, Traits>::
postRegistrationSetup( typename Traits::SetupData  /* worksets */,
  PHX::FieldManager<Traits>&  fm)
{
  using namespace PHX;
  this->utils.setFieldData(_fieldValue,fm);

  TEUCHOS_ASSERT(static_cast<std::size_t>(_fieldValue.extent(2)) == _size);

  if( _isConstant ) {
    auto val = _value->fetch();
    if( _size==1 ) {
      _fieldValue.deep_copy(val[0]);
    } else {
      for (int cell = 0; cell < _fieldValue.extent_int(0); ++cell)
        for (int ip = 0; ip < _fieldValue.extent_int(1); ++ip) 
          for (int dim = 0; dim < _size; ++dim) {
            _fieldValue(cell,ip, dim) = val[dim];
      }
    }
  }
}

//**********************************************************************
template<typename EvalT, typename Traits>
void
VariableEvaluator<EvalT, Traits>::
evaluateFields( typename Traits::EvalData  /* d */)
{
  if( !_isConstant ) {
    auto val = _value->fetch();
    if( _size==1 ) {
      _fieldValue.deep_copy(val[0]);
    } else {
      for (int cell = 0; cell < _fieldValue.extent_int(0); ++cell)
        for (int ip = 0; ip < _fieldValue.extent_int(1); ++ip) 
          for (int dim = 0; dim < _size; ++dim) {
            _fieldValue(cell,ip, dim) = val[dim];
      }
    }
  }
  
}

template<typename EvalT>
Teuchos::RCP< std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > >
MaterialEvalFactory<EvalT>::
buildEval(const std::vector< Teuchos::RCP<panzer::PhysicsBlock> > & physicsBlocks,
           CMaterials& materials ) const
{
  Teuchos::RCP< std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > > evaluators = 
    Teuchos::rcp(new std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > );

  for( auto pb: physicsBlocks ) {
    auto eb = pb->elementBlockID();
    //std::string matl = eb.MaterialID();
    std::string matl("mat0");
    auto cmatl = materials[matl];
    auto pir = pb->getIntegrationRules();
    auto ir = pir[pir.begin()->second->order()];
    //dl_scalar = Teuchos::rcp(new PHX::MDALayout<Cell,IP>(workset_size,num_points));
    int wksize = ir->dl_scalar->extent(0);
    int np = ir->dl_scalar->extent(1);
    for( auto itr = cmatl->items.begin(); itr!=cmatl->items.end(); ++itr )  {
      std::string item_name = itr->first; 
      std::cout << "aaaaaaaa=" << item_name << std::endl;
  //    auto aa = VariableEvaluator<EvalT,panzer::Traits>(itr->second(),itr->first(),wksize,np,1);
  //    auto aval = std::make_shared<VariableEvaluator<EvalT,panzer::Traits>>(itr->second(),itr->first(),wksize,np,1);
  //    evaluators->emplace_back(aval);
    }
  }

  return evaluators;
}

// *******************************************************************
/*template<typename EvalT, typename Traits>
void 
buildMaterialEvaluators( CMaterialEvals<EvalT,Traits>&  matEvals, std::vector<Teuchos::RCP<panzer::PhysicsBlock> > & physicsBlocks,
                          CMaterials& materials, std::vector<std::string> pblockNames )
{
  matEvals.clear();
  for( auto pb: physicsBlocks ) {
    auto eb = pb->elementBlockID();
    // std::string matl = eb.MaterialID();
    std::string matl("eblock-0_0");
    auto cmatl = materials[matl];
    auto pir = pb->getIntegrationRules();
    auto ir = pir[pir.begin()->second->order()];
    //dl_scalar = Teuchos::rcp(new PHX::MDALayout<Cell,IP>(workset_size,num_points));
    int wksize = ir->dl_scalar->extent(0);
    int np = ir->dl_scalar->extent(1);
    for( auto itr = cmatl->begin(); itr!=cmatl->end(); ++itr )  {
      auto aval = std::make_shared<VariableEvaluator<EvalT,Traits>>(itr->second(),itr->first(),wksize,np,1);
      matEvals.emblace_back(aval);
    }
  }
}
*/

//**********************************************************************
/*
void 
buildMaterialEvals(const Teuchos::ParameterList& p, const Teuchos::RCP<panzer::IntegrationRule>& ir)
{
  Teuchos::ParameterList input;

  for (auto it=p.begin(); it!= p.end(); ++it) {
    TEUCHOS_TEST_FOR_EXCEPTION( !(it->second.isList()), std::logic_error,
				"Error - All objects in the Material sublist must be unnamed sublists!" );
    Teuchos::ParameterList& sublist = Teuchos::getValue<Teuchos::ParameterList>(it->second);
    
    std::string matl_name = sublist.get<std::string>("Name");
    for(auto iti = sublist.begin(); iti != sublist.end(); ++iti) {
      if( iti->first=="Name" ) continue;       // not a sublist
      if( !iti->second.isList() ) continue;       // not a sublist
      auto pl = sublist.sublist(iti->first);
      pl.set("Field Name", matl_name+"_"+pl.name());
      pl.set("Data Layout", ir->dl_scalar);
    //     items[it->first] = std::make_unique<variable<>>(pl);
    }
   // materials[matl_name] = std::make_shared<Material>(sublist);
  }
}
*/

//**********************************************************************
template <typename EvalT,typename Traits>
MaterialEvaluator<EvalT,Traits>::MaterialEvaluator(const std::string & name, const std::shared_ptr<Material> m,
        const panzer::IntegrationRule & ir) : _name(name), pmatl(m)
{
  _np = m->size(name);

  if( _np>1 ) {
    Teuchos::RCP<PHX::DataLayout> data_layout = 
      Teuchos::rcp(new PHX::MDALayout<panzer::Cell,panzer::IP,panzer::Dim>(ir.workset_size, ir.num_points, _np));
    param = PHX::MDField<ScalarT,panzer::Cell, panzer::IP, panzer::Dim >(name, data_layout);
    this->addEvaluatedField(param);
  } else if( _np==1 ){
    param = PHX::MDField<ScalarT,panzer::Cell, panzer::IP >(name, ir.dl_scalar);
    this->addEvaluatedField(param);
  } else   {
    /* not found: wrong */
  }
  
}

//**********************************************************************
template <typename EvalT,typename Traits>
void MaterialEvaluator<EvalT,Traits>::evaluateFields(typename Traits::EvalData workset)
{ 
  using panzer::index_t;
  for (index_t cell = 0; cell < workset.num_cells; ++cell) {
    for (int point = 0; point < param.extent(1); ++point) {
      auto val = pmatl->fetch(_name);
      for( unsigned int n=0; n<_np; n++ )
        param(cell,point,n) = val[n];
    }
  }
}

// ********************************************************************
template<typename EvalT>
Teuchos::RCP< std::vector< Teuchos::RCP<PHX::Evaluator<panzer::Traits> > > > 
MaterialEvaluatorFactory<EvalT>::
buildClosureModels(const std::string& matlName,
		   const Teuchos::ParameterList& pmatl, 
		   const panzer::FieldLayoutLibrary& fl,
		   const Teuchos::RCP<panzer::IntegrationRule>& ir,
		   const Teuchos::ParameterList& /* default_params */,
		   const Teuchos::ParameterList& /* user_data */,
		   const Teuchos::RCP<panzer::GlobalData>& /* global_data */,
		   PHX::FieldManager<panzer::Traits>& /* fm */) const
{
  using Teuchos::RCP;
  using Teuchos::rcp;
  using Teuchos::ParameterList;

  RCP< std::vector< RCP<PHX::Evaluator<panzer::Traits> > > > evaluators = 
    rcp(new std::vector< RCP<PHX::Evaluator<panzer::Traits> > > );

  std::shared_ptr<Material> pMaterial_= std::make_shared<Material>(matlName, pmatl);
  Teuchos::RCP< PHX::Evaluator<panzer::Traits> > e = 
        Teuchos::rcp(new MaterialEvaluator<EvalT,panzer::Traits>(matlName,pmatl,*ir));

  evaluators->push_back(e);

  return evaluators;
}

}

#endif
